/* All Player V Monster Attacking Related Stuff */

PATTACK // The player's attack datum? Default is bare hands.
	var
		dietype = 2 as num
		ndice = 1 as num
		dbonus = 0 as num

// struct SPECIALATTACK rgspecialattack[specialAttackMax];

SPECIALATTACK // Not too sure what this is, yet. rgspecialattack is related to it in a fashion
	var
		mcat // Monster category this applies to
		//foo // Was a union that contained the three vars below.
		ptohit /* plus to hit */
		ptodam /* plus to damage */
		action /* special action */

MONSUBTYPEINFO // Info common to all monsters of a type
	var
		level as num
		ac as num
		range /* range at which creature can detect player and wake - not sure of usefulness */
		visible /* range at which creature is visible - really not sure of usefulness */
		experience as num
	//	sound/iSound /* Sound to use for this monster. :) */
		attributes /* really lots of bits - a DWORD */
		category /* really lots of bits - a WORD */
		descattrs /* bits for what hit/miss descriptions are appropriate - use?? */
	New()
		level = args[1]
		ac = args[2]
		range = args[3]
		visible = args[4]
		experience = args[5]
		//iSound = args[6] - Sound not currently in use. Placeholder for future.
		attributes = args[7]
		category = args[8]
		descattrs = args[9]

//Monsters are of type NPMONSTER for these procs, apparently
mob/pc //These are things that players do(at the moment, anyways)
	proc
/*********************************************************************
DoPlayerAttack(mob/M as mob)
The player attacks the monster using current attack info...
*********************************************************************/
		DoPlayerAttack(mob/monster/M as mob)
			if(!M)
				world.log << "Combat: DoPlayerAttack failed. No mob passed."
				//Large Error Message Goes Here
				return 0
			ResetMonStat(M, mstatWandering)
			// TODO: Check for 2handed weaponry here
			if(FPlayerHit(M))
				src << "You hit [M]"
				var/fFirstAttack = (!FMonStat(M, mstatAttacked))
				SetMonStat(M, (mstatAwake | mstatHostile | mstatAttacked))
				ResetMonStat(M, mstatSlept) // Cancel mgical sleep
				if(fFirstAttack) // Alert other monsters and other stuff
					world.log << "Need to alert monsters about attack"
					// TODO: Alert some monsters in a certain area depending on certain values
					// Code will NOT be ported from CotW.
				// TODO: Figure out what the REVIEW #ifdefs are for
				// Possibly^W Probably output hit messages here
				M.AVMod -= AVMODPERATTACK //Modify the AVMod variable by the AVMODPERATTACK define
				// TODO: Ask Rick Saada(or check code) what AVMod is, etc
				DoPlayerHit(M) //Actually hit the monster
			else
				src << "You miss [M]"
				if(!FMonStat(M, mstatAttacked)) //If first attack, alert other monsters
					// TOTO: Alert some monsters in a certain area depending on certain values
					// Code will NOT be ported from CotW
					world.log << "Need to alert monsters about attack"
				SetMonStat(M, (mstatHostile | mstatAttacked))
				// TODO: Figure out what the REVIEW #ifdefs are for
				// Possibly^W Probably output miss messages here
				M.AVMod -= AVMODPERATTACK



/*********************************************************************
FPlayerHit(mob/M as mob)
A level 1 player has a 50% chance to hit a mnster with ac 10.
Chance goes up 5% per level, and down 5% per ac of monster. Attack
bonus adds 5% per plus. There is always a 1% chance of hitting the monster.
*********************************************************************/
		FPlayerHit(mob/monster/M as mob)
			var/tohit = 0
			var/specToHit = 0
			if(src.cSpecialAttacks) //cSpecialAttacks is number of special attacks a player has
				//Possibly list containing special attacks for attack bonuses against particular monsters
				// TODO : Fix loop numbers.
				for(var/iSpecialAttack = 0, iSpecialAttack < specialAttackMax, iSpecialAttack++)
					var/SPECIALATTACK/Attack = rgspecialattack[iSpecialAttack]
					if(M.FMonCat(Attack.mcat)) //See if monster can be affected by spec. attack
						specToHit += Attack.ptohit //Increase chances of hitting by listed amount
			tohit = (9 + src.level - M.ACFromMonster()) * 5 + src.tohit + specToHit - M.AVMod //Calculate hitting chances
			if(tohit < 1)
				tohit = 1
			return prob(tohit) //Randomly see if hit makes contact

/*********************************************************************
DoPlayerHit(mob/M as mob)
Calculate damage for a hit on monnster... call FDeadDoDamageToMonster to
apply to monster.
*********************************************************************/
		DoPlayerHit(mob/monster/M as mob)
			var damage, dieType
			/* calculate damage */
			dieType = pattack.dietype
			damage = pattack.ndice + pattack.dbonus
			/* always 1 hp per die, and stuff like 2-9 (1d8 + 1) */
			damage += roll(pattack.ndice, dieType)
			damage += todam                /* strength, magic, evil admin powers, etc */
			if(cSpecialAttacks) // If player has special attacks?
				for(var/iSpecialAttack = 0, iSpecialAttack < specialAttackMax, iSpecialAttack++)
					var/SPECIALATTACK/Attack = rgspecialattack[iSpecialAttack]
					if(M.FMonCat(Attack.mcat)) //See if monster can be affected by spec attack
						damage += Attack.ptodam //Increase damage by specified amount
			if(damage <= 0)
				damage = 1 /* even if major league cursed, at least 1 point */
			FDeadDoDamageToMonster(M, damage, attackNormalHit, true, true) //Hurt the monster, and see if it dies

/*********************************************************************
FDeadDoDamageToMonster(mob/M as mob, damage, attackType, fPlayer, fDoHitDesc)
If monster isn't immute to attacks of attackType, do damage to it
*********************************************************************/
		FDeadDoDamageToMonster(mob/monster/M as mob, damage, attackType, fPlayer, fDoHitDesc)
		//	var/fDoResistDesc = false // RE ADD IN FUTURE
			var/fReturn = false
			if(!M)
				world.log << "Combat: FDeadDoDamageToMonster failed. No mob passed."
				//Large Error Message Goes Here
				return 0
			if(M.FMonsterImmune(attackType)) // Check for monster immunity to attackType
				/* Output immunity message */
				if(fDoHitDesc)
					// HACK : Need to get real cotw-like messages!
					oview(src) << "[src] hit [M], but there was no effect"
					src << "You hit [M] without effect"
					// Output Hit message here
				return fReturn
			else
				var/newdamage = M.FMonsterResists(attackType, damage)
				if(newdamage) //WARNING! damage is &damage, and likely a pointer type.
					damage = newdamage
					// HACK : Need to get real cotw-like messages!
					oview(src) << "[src] hit [M] causing some damage"
					src << "You hit [M] causing some damage"
					// Output Monster Resistance message here
					//fDoResistDesc = True
			M.hp -= damage

			if(M.hp <=0)
				/* Give the player experience for killing the monster here */
				if(fDoHitDesc)
					// HACK : Need to get real cotw-like messages!
					oview(src) << "[src] has killed [M]!"
					src << "You have killed [M]!"
					// Inform player that they hit monster and killed it? (describe)
				if(fPlayer) //If a player, and not a monster did it
					GainExperience(M.ExpFromMonster())
				KillMonster(M)
				fReturn = True
		//	else  // RE ADD IN FUTURE
		//		if(fDoHitDesc)
					// Inform player that they hit monster but it survived? (describe)*/
			//	if(fDoResistDesc)
					// Inform player that monster resisted? (describe)
			return fReturn

/*********************************************************************
KillMonster(mob/M as mob)
Kill the monster, Drop any goodies it may be carrying.
*********************************************************************/
		KillMonster(mob/monster/M as mob) // TODO: Find a better place for this proc(where is it under)
			if(!M)
				world.log << "Combat: KillMonster failed. No mob passed."
				//Large Error Message Goes Here
				return 0
			/* Do any special processing. Output kill message */
			if(M.contents.len)
			/* This code possibly replaceable by nice DM stuff. Needs testing. */
			//	if(!(M.loc.contents.len > 1)) // If the monster's the only guy there,
			//		M.loc.contents = M.contents  // just transfer his stuff over
			//	else // Transfer stuff one at a time
				//M.loc.contents.Add(M.contents)
				//M.contents = null
				//Not sure if that would work or not, needs testing
				//** Definitely will work catch-all(some lag)
				for(var/atom/movable/A in M.contents)
					A.loc = M.loc
			M.TurnController.RemoveMob(M) // Remove monster from surreal time system
			var/LevelController/LC = LevelControllers[M.z]
			LC.MonsterController.MonsterDeath(M)
			del M
			// TODO: ****** DELETE MONSTER HERE!!! INFORM MONSTERCONTROLLER ABOUT DEATH!!! ******


mob/pc
	proc
		GainExperience(exp)
			src.experience += exp
			if(src.level < PlayerLevelMax - 1)
				while(src.experience >= ExpForNextLevel())
					GainLevel()
			if(src.experience > src.experienceMax)
				src.experienceMax = src.experience

		ExpForNextLevel()
			return ((30 << src.level) - 40)

		ExpForLastLevel()
			return ((30 << (src.level-1)) - 40)

		GainLevel()
			// TODO : FINISH THIS PROC
			// Hack : Proc needs finishing
			src.level += 1
			src << "You gained a level!"
/*			var/hpGain =  4 + rand(5) 	/* gain 4-8 hp */
			int stat;
			int manaGain = 2 + random(4)	/* gain 2-5 mana */
			char rgch[cchMaxSz];
			FARPROC	lpfnDlgProc;

			if ((stat = player.statCur[CONSTITUTION]) > 56)
				hpGain += (stat - 56) / 4;		/* plus constitution bonuses */
			else if (stat < 32)
				{
				hpGain -= (32 - stat) / 4;
				hpGain = max (hpGain, 1);	/* always at least 1 */
				}
			if ((stat = player.statCur[INTELLIGENCE]) > 64)
				manaGain +=  (stat - 64) / 3;	/* plus intelligence bonuses */
			else if (stat < 32)
				manaGain -= (32 - stat) / 4;

			manaGain = max (manaGain, 1);	/* at least 1 ! */

			player.hp += hpGain;
			player.hpMax += hpGain;
			player.mana = (player.manaMax += manaGain);	/* mana restore at level gain */

			player.level++;
			InvalidateStat(statHP);
			InvalidateStat(statMana);
			OutDescSz(IdstrSprintf1(rgch, IDTMPLTGainLevel, (WORD)player.level), fTrue);
			// let the player pick
			for (stat = 0; stat < spellMaxPlayer; stat++)
				{	// just using stat as a free index variable.
				if (rgSpell[stat].bLevel > (BYTE)(player.level >> 1))
					break;	// No more spells to learn at current level
				if (rgSpell[stat].bMana == -1)
					{		// still spells left to learn
					if(lpfnDlgProc = MakeProcInstance (LearnSpellDlgProc, hInstanceCur))
						{
						DialogBox ( hInstanceCur,
							MAKEINTRESOURCE(DLG_LEARNSPELL), GetActiveWindow(), lpfnDlgProc);
						FreeProcInstance(lpfnDlgProc);
						}
					break;
					}
				}
			RecalcSpellMana();	// Level affects mana
*/

mob/pc
	proc
		LoseExperience(exp)
			src.experience -= exp
			if(src.experience <= 0)
				// TODO: **** Cause player to die / lose ****
				return
			while((src.level > 1) && (src.experience < ExpForLastLevel()))
				LoseLevel()

		LoseLevel()
			// TODO: FINISH THIS PROC
			// Hack: Proc needs finishing
			src.level -= 1
			src << "You lost a level!"
/*	int hpLoss =  (4 + random(5));	/* gain 4-8 hp */
	int stat;
	int manaLoss = (1 + random(4));	/* gain 1-4 mana */
	char rgch[cchMaxSz];

	if ((stat = player.statCur[CONSTITUTION]) > 56)
		hpLoss += (stat - 56) / 4;		/* plus constitution bonuses */
	if ((stat = player.statCur[INTELLIGENCE]) > 64)
		manaLoss +=  (stat - 64) / 4;	/* plus intelligence bonuses */

	player.hp -= hpLoss;
	player.hpMax -= hpLoss;
	player.manaMax -= manaLoss;
	player.mana -= manaLoss;

	player.level--;
	InvalidateStat(statHP);
	InvalidateStat(statMana);
	OutDescSz(IdstrSprintf1(rgch, IDTMPLTLoseLevel, (WORD)player.level), fTrue);
	RecalcSpellMana();	// Level affects mana
	if (player.hp <= 0)
		SetGameState(stateJustDied);*/


mob/monster
	proc
		FMonCat(mcat)
			var/MONSUBTYPEINFO/subtype = rgmonsubtypeinfo[src.subType]
			return (subtype.category & mcat)

		FMonAttrib(mattrib)
			var/MONSUBTYPEINFO/subtype = rgmonsubtypeinfo[src.subType]
			if((subtype.attributes & mattrib))
				return True
			else
				return False

		ACFromMonster()
			var/MONSUBTYPEINFO/subtype = rgmonsubtypeinfo[src.subType]
			return (subtype.ac)

		LevelFromMonster()
			var/MONSUBTYPEINFO/subtype = rgmonsubtypeinfo[src.subType]
			return (subtype.level)

		ExpFromMonster()
			var/MONSUBTYPEINFO/subtype = rgmonsubtypeinfo[src.subType]
			return (subtype.experience)

		FMonsterImmune(attackType)
			switch(attackType)
				if(attackClaw)
				if(attackBite)
				if(attackTrample)
				if(attackNormalHit)
				if(attackTailLash)
				if(attackThrowBoulder)
				if(attackThrowIceball)
				if(attackHurlTailSpike)
				if(attackBuffet)
				if(attackFireArrow)
					return (FMonAttrib(src, mattribINormal))
				if(attackBreathFire)
				if(attackFire)
					return (FMonAttrib(src, mattribIFlame))
				if(attackBreathCold)
				if(attackCold)
					return (FMonAttrib(src, mattribICold))
				if(attackBreathAcid)
				if(attackAcid)
					return (FMonAttrib(src, mattribIAcid))
				if(attackBreathLightning)
				if(attackLightning)
					return (FMonAttrib(src, mattribILightning))
				if(attackBreathPoison)
				if(attackPoison1)
				if(attackPoison2)
				if(attackPoison3)
				if(attackDisease)
					return (FMonAttrib(src, mattribIPoison))
				if(attackFear)
					return (FMonAttrib(src, mattribIFear))
				if(attackConfusion)
					return (FMonAttrib(src, mattribIConfusion))
				if(attackSlow)
				if(attackBlind)
				if(attackSpell)
				if(attackExpDrain)
				if(attackStatDrain)
				if(attackManaDrain)
				if(attackHPDrain)
					return (FMonAttrib(src, mattribRMagic))
				if(attackObjDrain)
				if(attackObjDestroy)
				if(attackObjSteal)
				if(attackParalysis)
				if(attackWhirlwind)
					return False
				else
					world.log << "Combat: FMonsterImmune:Bad atype"
					return 0

		FMonsterResists(attackType, damage)
			switch(attackType)
				if(attackClaw)
				if(attackBite)
				if(attackTrample)
				if(attackNormalHit)
				if(attackTailLash)
				if(attackThrowBoulder)
				if(attackThrowIceball)
				if(attackHurlTailSpike)
				if(attackBuffet)
				if(attackFireArrow)
					if(FMonAttrib(src, mattribRNormal))
							/* yes, half damage */
						damage >>=1
						return damage
					return False
				if(attackBreathFire)
				if(attackFire)
					if(FMonAttrib(src, mattribRFlame))
							/* yes, half damage */
						damage >>=1 ;
						return damage
					else if(FMonCat(src, mcatColdUsing))
						damage = damage * 4 / 3
						return damage
					return False
				if(attackBreathCold)
				if(attackCold)
					if(FMonAttrib(src, mattribRCold))
							/* yes, half damage */
						damage >>=1
						return damage
					else if(FMonCat(src, mcatFireUsing))
						damage = damage * 4 / 3;
						return damage
					return False
				if(attackBreathAcid)
				if(attackAcid)
					if(FMonAttrib(src, mattribRAcid))
							/* yes, half damage */
						damage >>=1
						return damage
					else if(FMonCat(src, mcatLightningUsing))
						damage = damage * 4 / 3;
						return damage
					return False
				if(attackBreathLightning)
				if(attackLightning)
					if(FMonAttrib(src, mattribRLightning))
							/* yes, half damage */
						damage >>=1
						return damage
					return False
				if(attackBreathPoison)
				if(attackPoison1)
				if(attackPoison2)
				if(attackPoison3)
				if(attackDisease)
					if(FMonAttrib(src, mattribRPoison))
							/* yes, half damage */
						damage >>=1
						return damage
					return False
				if(attackFear)
				if(attackConfusion)
				if(attackSlow)
				if(attackBlind)
				if(attackSpell)
				if(attackExpDrain)
				if(attackStatDrain)
				if(attackManaDrain)
				if(attackObjDrain)
				if(attackObjDestroy)
				if(attackObjSteal)
				if(attackParalysis)
				if(attackHPDrain)
				if(attackWhirlwind)
					return False
				else
					world.log << "Combat: FMonsterResists:Bad atype"
					return 0


/*BOOL FMonAttrib(pmonster, mattrib)
NPMONSTER pmonster;
DWORD mattrib;
{
	// brain dead compiler screws up if I just return &
	if (rgmonsubtypeinfo[pmonster->subType].attributes & mattrib)
		return fTrue;
	else
		return (fFalse);
}
BOOL FMonCat(pmonster, mcat)
NPMONSTER pmonster;
WORD mcat;
{
	return (rgmonsubtypeinfo[pmonster->subType].category & mcat);
}
BYTE RangeFromPmonster(pmonster)
NPMONSTER pmonster;
{
	return (rgmonsubtypeinfo[pmonster->subType].range);
}
BYTE VisibleFromPmonster(pmonster)
NPMONSTER pmonster;
{
	return (rgmonsubtypeinfo[pmonster->subType].visible);
}
int ACFromPmonster(pmonster)
NPMONSTER pmonster;
{
	return (rgmonsubtypeinfo[pmonster->subType].ac);
}
int LevelFromPmonster(pmonster)
NPMONSTER pmonster;
{
	return (rgmonsubtypeinfo[pmonster->subType].level);
}
int ExpFromPmonster(pmonster)
NPMONSTER pmonster;
{
#ifdef DIFFICULTY
	return (
		rgmonsubtypeinfo[pmonster->subType].experience
		* (100 - params.PercentExpPerDiff * wGameDifficulty) / 100
		);
#else
	return (rgmonsubtypeinfo[pmonster->subType].experience);
#endif /* DIFFICULTY */
}*/